#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>
#include "Log.hpp"

using namespace std;

typedef function<string(const string &)> func_t;

string defaultip = "0.0.0.0";

Log lg;

enum
{
    SOCKET_CREATE = 1,
    BIND_CREATE
};

class UdpServer
{
public:
    UdpServer(uint16_t port, string ip = defaultip, bool isrunning = false)
        : _ip(ip), _port(port), _isrunning(isrunning)
    {
        ;
    }

    void Init()
    {
        // 1.创建socket
        int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socketfd == -1)
        {
            lg(Fatal, "create socket fail,errno:%d,message:%s", errno, strerror(errno));
            exit(SOCKET_CREATE);
        }
        lg(Info, "create socket success,sokcet:%d", socketfd);
        _sockfd = socketfd;

        // 2.socket与服务端连接,bind
        sockaddr_in sockin;
        bzero(&sockin, sizeof(sockin));

        sockin.sin_family = AF_INET;
        sockin.sin_port = htons(_port);
        sockin.sin_addr.s_addr = inet_addr(_ip.c_str()); // inet_addr可以将ip从字符串转为整型(uint32_t)，并符合网络字节序

        socklen_t len = sizeof(sockin);
        int res = bind(_sockfd, (const sockaddr *)&sockin, len);
        if (res == -1)
        {
            lg(Fatal, "Server bind fail,errno:%d,message:%s", errno, strerror(errno));
            exit(BIND_CREATE);
        }
        lg(Info, "bind success,errno:%d,message:%s", errno, strerror(errno));
    }

    // void Run(func_t handler)
    // {
    //     _isrunning = true;
    //     char buffer[1024];

    //     while (_isrunning)
    //     {
    //         sockaddr_in client;
    //         socklen_t len = sizeof(client);

    //         ssize_t num = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&client, &len);
    //         if (num == -1)
    //         {
    //             lg(Error, "receive infomation fail,errno:%d,message:%s", errno, strerror(errno));
    //             continue;
    //         }

    //         buffer[num] = '\0';
    //         cout << buffer << endl;

    //         string reply = handler(buffer);

    //         int res = sendto(_sockfd, reply.c_str(), reply.size(), 0, (sockaddr *)&client, len);
    //         if (res == -1)
    //         {
    //             lg(Error, "server send to client fail,errno:%d,message:%s", errno, strerror(errno));
    //             continue;
    //         }
    //     }
    // }

    void CheckUser(const sockaddr_in &client, string clientIp, uint16_t clientPort)
    {
        auto res = _online_user.find(clientIp);
        if (res == _online_user.end())
        {
            _online_user[clientIp] = client;
            cout << "[" << clientIp << ":" << clientPort << "] add to online user" << endl;
        }
    }

    void BroadCast(string info, string clientIp, uint16_t clientPort)
    {
        for (auto &user : _online_user)
        {

            string message = "[";
            message += clientIp;
            message += ":";
            message += to_string(clientPort);
            message += "]# ";
            message += info;

            socklen_t len = sizeof(user);
            int res = sendto(_sockfd, message.c_str(), message.size(), 0, (sockaddr *)(&(user.second)), len);
            if (res == -1)
            {
                lg(Error, "server send to client fail,errno:%d,message:%s", errno, strerror(errno));
                continue;
            }
        }
    }

    void Run()
    {
        _isrunning = true;
        char buffer[1024];

        while (_isrunning)
        {
            sockaddr_in client;
            socklen_t len = sizeof(client);

            ssize_t num = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&client, &len);
            if (num == -1)
            {
                lg(Error, "receive infomation fail,errno:%d,message:%s", errno, strerror(errno));
                continue;
            }

            buffer[num] = '\0';

            string clientIp = inet_ntoa(client.sin_addr);
            uint16_t clientPort = ntohs(client.sin_port);
            CheckUser(client, clientIp, clientPort);

            BroadCast(buffer, clientIp, clientPort);
            // cout << buffer << endl;

            // string reply = handler(buffer);

            // int res = sendto(_sockfd, reply.c_str(), reply.size(), 0, (sockaddr *)&client, len);
            // if (res == -1)
            // {
            //     lg(Error, "server send to client fail,errno:%d,message:%s", errno, strerror(errno));
            //     continue;
            // }
        }
    }

private:
    int _sockfd;
    string _ip;
    uint16_t _port;
    bool _isrunning;
    unordered_map<string, sockaddr_in> _online_user;
};